var GameEntity = require('js/gameEntity').GameEntity;
var Match = require('js/match').Match;
var Map = require('js/map').Map;
var gamejs = require('gamejs');

qModule('js/gameEntity', {
	setup: function() {
		// Set match and map
		this.match = new Match();
		this.map = new Map(this.match, 800, 400);
		this.match.setMap(this.map);
		// Bootstrap the GameEntity
		this.center = [100, 100];
		this.upgradeList = [
			// Level 0
			{ image: IMAGE_ROOT + 'enemy.png',
			imageShape: 'rectangular',
			myProperty: 100,
			price: 100,
			resaleValue: 100
			},
			// Level 1
			{ image: IMAGE_ROOT + 'turret.png',
			imageShape: 'circular',
			myProperty: 1000,
			price: 1000
			}
		];
		this.gE = new GameEntity(this.match, this.center, this.upgradeList);
	}
});

test("GameEntity(match, center, upgradeList, fillsMap, startingLvl)",
	function() {
	// Test constructor with defaults values (where applicable)
	ok(this.gE instanceof GameEntity,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, default, default): " +
		"object correctly created."
	);
	deepEqual(this.gE.getMatch(), this.match,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, default, default): " +
		"match correctly setted."
	);
	deepEqual(this.gE.getCenter(), this.center,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, default, default): " +
		"center correctly setted."
	);
	deepEqual(this.gE.getUpgradeList(), this.upgradeList,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, default, default): " +
		"upgradeList correctly setted."
	);
	deepEqual(this.gE.fillsMap(), false,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, default, default): " +
		"default fillsMap correctly setted."
	);
	deepEqual(this.gE.getLevel(), 0,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, default, default): " +
		"default startingLvl correctly setted."
	);
	// Test constructor with non defaults values (where applicable)
	var userGE = new GameEntity(this.match, this.center, this.upgradeList,
		true, 1
	);
	ok(userGE instanceof GameEntity,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number): " +
		"object correctly created."
	);
	deepEqual(userGE.getMatch(), this.match,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number): " +
		"match correctly setted."
	);
	deepEqual(userGE.getCenter(), this.center,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number): " +
		"center correctly setted."
	);
	deepEqual(userGE.getUpgradeList(), this.upgradeList,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number): " +
		"upgradeList correctly setted."
	);
	deepEqual(userGE.fillsMap(), true,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number): " +
		"fillsMap correctly setted."
	);
	deepEqual(userGE.getLevel(), 1,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number): " +
		"startingLvl correctly setted."
	);
	// Check that RangeError is throwed when using an invalid startingLvl
	throws(
		function() {
			new GameEntity(this.match, this.center, this.upgradeList,
				true, -1
			);
		},
		RangeError,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number < 0): " +
		"exception correctly throwed with a negative startingLvl."
	);
	throws(
		function() {
			var maxLevel = this.upgradeList.length - 1;
			new GameEntity(this.match, this.center, this.upgradeList,
				true, maxLevel +1
			);
		},
		RangeError,
		"Test with arguments " +
		"(Match, [Number, Number], upgradeList, Boolean, Number > maxLvl): " +
		"exception correctly throwed with a startingLvl > maximum level."
	);
});

test("GameEntity.getMatch()", function() {
	deepEqual(this.gE.getMatch(), this.match, "Works correctly!");
});

test("GameEntity.getMatchMap()", function() {
	deepEqual(this.gE.getMatchMap(), this.map, "Works correctly!");
});

test("GameEntity.getCenter()", function() {
	deepEqual(this.gE.getCenter(), this.center, "Works correctly!");
});

test("GameEntity.getRect()", function() {
	// This one is somewhat useless
	deepEqual(this.gE.getRect(), this.gE.rect, "Works correctly!");
});

test("GameEntity.fillsMap()", function() {
	// Default is false
	deepEqual(this.gE.fillsMap(), false, "Works correctly!");
});

test("GameEntity.pointsOnMap()", function() {
	// These tests rely on _circlePoints and _rectPoints internal methods
	// Test with rectangular imageShape
	deepEqual(this.gE.pointsOnMap(), this.gE._rectPoints(),
		"Works correctly on a GameEntity with imageShape='rectangular'."
	);
	// Test with a circular imageShape
	this.gE.getLevelSettings().imageShape = 'circular';
	deepEqual(this.gE.pointsOnMap(), this.gE._circlePoints(),
		"Works correctly on a GameEntity with imageShape='circular'."
	);
	// Test with an undefined imageShape
	this.gE.getLevelSettings().imageShape = undefined;
	deepEqual(this.gE.pointsOnMap(), this.gE._rectPoints(),
		"Works correctly on a GameEntity with an undefined imageShape."
	);
	// Test with an invalid imageShape
	this.gE.getLevelSettings().imageShape = 'romboidal';
	deepEqual(this.gE.pointsOnMap(), this.gE._rectPoints(),
		"Works correctly on a GameEntity with an invalid imageShape."
	);
});

test("GameEntity.getLevel()", function() {
	// Default staring level is 0
	deepEqual(this.gE.getLevel(), 0, "Works correctly!");
});

test("GameEntity.isAtMaximumLevel()", function() {
	// Test on a GameEntity not at its maximum level
	ok( ! this.gE.isAtMaximumLevel(),
		"Works correctly on a GameEntity not at maximum level."
	);
	// Test on a GameEntity at maximum level
	this.gE.upgrade();
	ok( this.gE.isAtMaximumLevel(),
		"Works correctly on a GameEntity at maximum level."
	);
});

test("GameEntity.canUpgrade()", function() {
	var match = this.gE.getMatch();
	// Enough money and available level
	ok(this.gE.canUpgrade(),
		"Works with enough credits - GameEntity not at maximum level"
	);
	// Not enough money and GameEntity already at Maximum level
	match.changeCredit(- match.getCredit());  // 0 credits
	ok( ! this.gE.canUpgrade(),
		"Works without enough credits - GameEntity at maximum level"
	);
	// GameEntity not at maxium level but not enough money
	var gE = new GameEntity(this.match, this.center, this.upgradeList);
	ok( ! gE.canUpgrade(),
		"Works without enough credits - GameEntity not at maximum level"
	);
	// Enough money but GameEntity already at maximum level
	match.changeCredit(100000);
	this.gE.upgrade();
	ok( ! this.gE.canUpgrade(),
		"Works with enough credits - GameEntity at maximum level"
	);
});

test("GameEntity.getUpgradeList()", function() {
	deepEqual(this.gE.getUpgradeList(), this.upgradeList,
		"Works correctly!"
	);
});

test("GameEntity.getLevelSettings()", function() {
	// Test without arguments (get the current level settings)
	deepEqual(this.gE.getLevelSettings(), this.upgradeList[0],
		"Test with argument (undefined): " +
		"current level settings correctly returned."
	);
	// Test without a numeric argument
	deepEqual(this.gE.getLevelSettings(1), this.upgradeList[1],
		"Test with argument (Number): " +
		"settings for another level correctly returned."
	);
	// Tests with invalid level numbers
	throws (
		function() { this.gE.getLevelSettings(-1); },
		RangeError,
		"Test with argument (Number < 0): " +
		"exception correctly throwed."
	);
	throws (
		function() {
			var maxLevel = this.gE.getUpgradeList().length - 1;
			this.gE.getLevelSettings(maxLevel + 1);
		},
		RangeError,
		"Test with argument (Number > Maximum Level Allowed): " +
		"exception correctly throwed."
	);
	throws (
		function() { this.gE.getLevelSettings("I'm not a number"); },
		RangeError,
		"Test with argument (String): " +
		"exception correctly throwed."
	);
});

test("GameEntity.getUpgradePrice()", function() {
	// Upgrade cost defined
	var gEUpgradeCost = this.upgradeList[1].price;
	deepEqual(this.gE.getUpgradePrice(), gEUpgradeCost,
		"Works correctly on a GameEntity with upgrade price set."
	);
	// undefined price
	this.upgradeList.push(
		{image: IMAGE_ROOT + 'enemy.png'}
	);
	this.gE.upgrade();
	deepEqual(this.gE.getUpgradePrice(), 0,
		"Works correctly on a GameEntity with undefined price."
	);
	// Exception correctly throwed at maximum level
throws(
		function() { this.gE.gEUpgradeCost(); },
		Error,
		"Exception correctly trowed by a GameEntity already at its " +
		"maximum level."
	);
});

test("GameEntity.upgrade()", function() {
	// Test on a GameEntity having the same image for two levels
	var sameImageUL = [
		{image: IMAGE_ROOT + 'enemy.png'}, {image: IMAGE_ROOT + 'enemy.png'}
	];
	var sameImageGE = new GameEntity(this.match, this.center, sameImageUL);
	sameImageGE.upgrade();
	deepEqual(sameImageGE.getLevel(), 1,
		"Test on a GameEntity with equal images across levels: " +
		"level number correctly increased."
	);
	throws(
		function() { sameImageGE.upgrade(); },
		Error,
		"Test on a GameEntity with equal images across levels: " +
		"exception correctly trowed while upgrading a GameEntity already " +
		"at its maximum level."
	);
	// this.gE was created with fillsMap = false, starts from level 0 and
	// the image of the second level is different from the one in the first
	// one
	this.gE.upgrade();
	deepEqual(this.gE.getLevel(), 1,
		"Test on a GameEntity with different images across levels: " +
		"level number correctly increased."
	);
	// Check that the new image of the new level get loaded
	var newLevelImage = gamejs.image.load(this.upgradeList[1].image);
	deepEqual(this.gE.image, newLevelImage,
		"Test on a GameEntity with different images across levels: " +
		"new image correctly loaded."
	);
	// Check that that also the rect gets updated
	deepEqual(this.gE.getRect().center, this.center,
		"Test on a GameEntity with different images across levels: " +
		"new rect correctly setted."
	);
	var rectSize = [this.gE.getRect().width, this.gE.getRect().height ];
	deepEqual(rectSize, newLevelImage.getSize(),
		"Test on a GameEntity with different images across levels: " +
		"new rect correctly setted."
	);
	// Check that the exception is correctly trowed when trying to upgrade
	// again (this.gE has already reached its maximum level)
	throws(
		function() { this.gE.upgrade(); },
		Error,
		"Test on a GameEntity with different images across levels: " +
		"exception correctly trowed while upgrading a GameEntity already " +
		"at its maximum level."
	);
	// Tests on a GameEntity created with fillsMap = true and not on Map
	var gE = new GameEntity(this.match, this.center, this.upgradeList,
		true /* Starting level is the default 0 */
	);
	gE.upgrade();
	deepEqual(gE.getLevel(), 1,
		"Test on a GameEntity created with fillsMap=true and not on Map: " +
		"level number correctly increased."
	);
	var points = gE.pointsOnMap();
	var cleared = true;
	for( var i = 0; i < points.length; i++ ) {
		cleared = cleared && ! this.map.isFilled(points[i]);
	}
	ok(cleared,
		"Test on a GameEntity created with fillsMap=true and not on Map: " +
		"points correctly left clear."
	);
	// Test that the exception is throwed when there insn't enough credits
	// for the upgrade
	this.match.changeCredit( - this.match.getCredit() );  // 0 credits
	gE = new GameEntity(this.match, this.center, this.upgradeList,
		true /* Starting level is the default 0 */
	);
	throws (
		function() { gE.upgrade(); },
		Error,
		"Test on a GameEntity created with fillsMap=true and on Map: " +
		"exception correctly throwed if there isn't enough credits for it."
	);
});

test("GameEntity.getResaleValue()", function() {
	// Test on a GameEntity with resaleValue correctly defined
	var currentResaleValue = this.upgradeList[0].resaleValue;
	deepEqual(this.gE.getResaleValue(), currentResaleValue,
		"Test on a GameEntity with resaleValue defined."
	);
	// Test on a GameEntity with undefined resaleValue
	this.gE.upgrade();
	deepEqual(this.gE.getResaleValue(), 0,
		"Test on a GameEntity with undefined resaleValue."
	);
});

test("GameEntity.updateImage()", function() {
	// updateImage is called by the GameEntity constructor
	var imageUl = gamejs.image.load(this.upgradeList[0].image);
	deepEqual(this.gE.image, imageUl,
		"Works fine!"
	);
});